/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using EvaluationException = System.Qizx.Api.EvaluationException;
using Item = System.Qizx.Api.Item;
using Node = System.Qizx.Api.Node;
using EvalContext = System.Qizx.XQuery.EvalContext;
using XQItem = System.Qizx.XQuery.XQItem;
using XQValue = System.Qizx.XQuery.XQValue;
namespace System.Qizx.XQuery.Dt
{
	
	/// <summary> A sequence that stores Items in an array. 
	/// Used for local variables holding a true Item sequence.
	/// TODO pb with scores: accumulate in array?
	/// </summary>
	public class ArraySequence:GenericValue
	{
		virtual public XQValue Origin
		{
			get
			{
				return origin;
			}
			
			set
			{
				this.origin = value;
			}
			
		}
		virtual public int Size
		{
			get
			{
				return size;
			}
			
		}
		virtual public double[] Scores
		{
			set
			{
				this.scores = value;
			}
			
		}
		internal const int MAX_BLOCK_SIZE = 65536;
		
		protected internal System.Object[] items; // not Item[] to avoid realloc after FLWR sort
		protected internal int size;
		
		protected internal int index = - 1;
		protected internal System.Collections.ArrayList overflow; // when growing too much, use overflow blocks
		protected internal XQValue origin; // expanded sequence
		
		protected internal double[] scores;
		
		
		public ArraySequence(System.Object[] items, int size)
		{
			this.items = (items == null)?null:(System.Object[]) items.Clone();
			this.size = size;
		}
		
		// constructor without copy: for bornAgain()
		public ArraySequence(int size, System.Object[] items, XQValue origin)
		{
			this.items = items;
			this.size = size;
			this.origin = origin;
		}
		
		public ArraySequence(int initialSize, XQValue origin)
		{
			items = new System.Object[initialSize];
			size = 0;
			this.origin = origin;
		}
		
		public virtual int append(XQValue sequence)
		{
			int count = 0;
			for (; sequence.Next(); ++count)
				addItem(sequence.Item);
			return count;
		}
		
		public static ArraySequence copy(XQValue sequence)
		{
			ArraySequence seq = new ArraySequence(8, sequence);
			seq.append(sequence);
			return seq;
		}
		
		
		public override XQValue BornAgain()
		{
			// TRICK: constructor w/o copy
			ArraySequence seq = new ArraySequence(size, items, origin);
			seq.scores = scores;
			return seq;
		}
		
		public override double GetFulltextScore(Item item)
		{
			if (item == null)
				item = GetNode();
			if (scores != null)
				return scores[index];
			if (origin != null)
				return origin.GetFulltextScore(item);
			return base.GetFulltextScore(item);
		}
		
		public virtual void  addItem(Item item)
		{
			if (item == null)
				return ; // no crap please
			if (size >= items.Length)
			{
				if (items.Length < MAX_BLOCK_SIZE)
				{
					System.Object[] old = items;
					items = new System.Object[old.Length * 2];
					Array.Copy(old, 0, items, 0, old.Length);
				}
				else
				{
					// avoid big blocks: accumulate blocks into a vector,
					// then use pack() to cleanup
					if (overflow == null)
						overflow = new System.Collections.ArrayList();
					overflow.Add(items);
					items = new System.Object[items.Length];
					size = 0;
				}
			}
			items[size++] = item;
		}
		
		public virtual void  addItems(Item[] addedItems, int count)
		{
			// -System.err.println("addItems "+count);
			if (size + count >= items.Length)
			{
				// OPTIM todo
				for (int i = 0; i < count; i++)
					addItem(addedItems[i]);
			}
			else
			{
				// most frequent case: no OVF
				Array.Copy(addedItems, 0, items, size, count);
				size += count;
			}
		}
		
		/// <summary> if addItem has been used, must be called at the end of the construction.</summary>
		public virtual void  pack()
		{
			if (overflow == null)
				return ;
			int nsize = overflow.Count * items.Length + size;
			System.Object[] nitems = new System.Object[nsize];
			int ptr = 0;
			for (int b = 0; b < overflow.Count; b++)
			{
				Array.Copy((Array)overflow[b], 0, nitems, ptr, items.Length);
				ptr += items.Length;
			}
			Array.Copy(items, 0, nitems, ptr, size);
			items = nitems;
			size = nsize;
			overflow = null;
			reset();
		}
		
		public override bool Next()
		{
			// automatic packing:
			if (overflow != null)
				pack();
			// -System.err.println(this+" ArraySEQ.next index "+index+" "+size);
			do 
			{
				if (index >= size - 1)
					return false;
				// here we *know* that we have Items (unlike ObjectArraySequence)
				item = (XQItem) items[++index];
			}
			while (item == null);
			return true;
		}
		
		public override long QuickCount(EvalContext context)
		{
			if (items == null)
				return 0;
			// no need to pack (assumes no duplication)
			int ovfsize = (overflow != null)?overflow.Count:0;
			return ovfsize * items.Length + size;
		}
		
		public override bool HasQuickIndex()
		{
			return true;
		}
		
		public override XQItem QuickIndex(long index)
		{
			return index <= 0 || index > size?null:(XQItem) items[(int) index - 1];
		}
		
		public virtual void  reset()
		{
			index = - 1;
		}
		
		public override bool WorthExpanding()
		{
			return false; // because it is already expanded!
		}
		
		/// <summary> Converts a Value representing a sequence of Nodes into an array.</summary>
		public static Node[] expandNodes(XQValue value_Renamed)
		{
			Node[] nodes = new Node[8];
			int ptr = 0;
			for (; value_Renamed.Next(); )
			{
				if (ptr >= nodes.Length)
				{
					Node[] old = nodes;
					nodes = new Node[old.Length * 2];
					Array.Copy(old, 0, nodes, 0, old.Length);
				}
				nodes[ptr++] = value_Renamed.GetNode();
			}
			// return a full array:
			if (ptr == nodes.Length)
				return nodes;
			Node[] rnodes = new Node[ptr];
			Array.Copy(nodes, 0, rnodes, 0, ptr);
			return rnodes;
		}
		
		/// <summary> Converts a Value representing a sequence of Nodes into an array.</summary>
		public static Item[] expand(XQValue value_Renamed)
		{
			Item[] items = new Item[8];
			int ptr = 0;
			for (; value_Renamed.Next(); )
			{
				if (ptr >= items.Length)
				{
					Item[] old = items;
					items = new Item[old.Length * 2];
					Array.Copy(old, 0, items, 0, old.Length);
				}
				items[ptr++] = value_Renamed.Item;
			}
			// return a full array:
			if (ptr == items.Length)
				return items;
			Item[] rnodes = new Item[ptr];
			Array.Copy(items, 0, rnodes, 0, ptr);
			return rnodes;
		}
	}
}